package node.func;

import grammar.Lexer;
import grammar.MyError;
import lexical.Key;
import lexical.Word;
import midcode.MidCode;
import midcode.MidcodeGenerator;
import midcode.OPType;
import node.Node;
import node.block.Block;
import node.func.param.FuncFParams;
import symbol.Function;
import symbol.SymbolTable;

import java.util.ArrayList;

public class FuncDef implements Node {
    private final Lexer lexer;
    private final SymbolTable table;
    private final Node father;
    private Word funcType;
    private Function function;
    private FuncFParams params;
    private Block block;
    public ArrayList<Node> nodes = new ArrayList<>();

    public FuncDef(Lexer lexer, SymbolTable table, Node father) {
        this.lexer = lexer;
        this.table = table;
        this.father = father;
        parseFuncDef();
    }

    private void parseFuncDef() {
        Word word = lexer.getWord(1);
        if (table.redefine(word.word)) {
            lexer.addError(new MyError(word.lineNum, 'b')); // error b
        }
        function = new Function(word, lexer.getWord(0).word);
        table.addFunction(function);
        funcType = lexer.getCurWord(); // void | int
        nodes.add(lexer.getCurWord()); // ident
        table.addParams = true;
        nodes.add(lexer.getCurWord()); // (
        if (!lexer.isWordType(0, Key.RPARENT) && !lexer.isWordType(0, Key.LBRACE)) {
            params = new FuncFParams(lexer, table, this, function);
            nodes.add(params);
        }
        if (lexer.isWordType(0, Key.RPARENT)) {
            nodes.add(lexer.getCurWord()); // )
        } else {
            lexer.addError(new MyError(lexer.getWord(-1).lineNum, 'j'));
        }
        block = new Block(lexer, table, this);
        nodes.add(block);
    }

    @Override
    public Node getFather() {
        return father;
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append(funcType.toString()).append("<FuncType>\n");
        for (Node word : nodes) {
            sb.append(word.toString());
        }
        return sb.append("<FuncDef>\n").toString();
    }

    @Override
    public String genMidCode() {
        MidcodeGenerator mg = MidcodeGenerator.getInstance();
        mg.startFuncDef(function.getName());
        mg.addMidcode(new MidCode(OPType.FUNC_DECL, null, null, function.getName()));
        if (params != null) {
            params.genMidCode();
        }
        if (block != null) {
            block.genMidCode();
        }
        if (funcType.key == Key.VOIDTK) {
            mg.addMidcode(new MidCode(OPType.RET_VOID, null, null, null));
        }
        table.setLocalValueOffset(mg.getFuncName());
        return "";
    }
}

